/*
 * Copyright 2007-2009 Thomas Krause (thomas@berlin-reloaded.de)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gidoo.cms.view;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.gidoo.cms.GidooCMS;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.pages.RedirectPage;
import org.apache.wicket.model.Model;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.request.target.resource.ResourceStreamRequestTarget;
import org.gidoo.cms.helper.Tools;
import org.gidoo.cms.helper.CMSResourceStreamWriter;
import org.gidoo.cms.jcr.JcrHelper;
import org.gidoo.cms.login.GidooCMSSession;

/**
 * Catches the data from the database, transforms using XSLT it and renders it 
 * for the visitor.<br>
 * <br>
 * If you want to customize the appearance of you webpage ("template") or if you 
 * want to add navigation, user-specific stuff etc. you should override this 
 * default class and should use the overridden class in the 
 * {@link org.gidoo.cms.GidooCMS#init} 
 * function.
 * @author <a href="mailto:thomas@berlin-reloaded.de">Thomas Krause</a>
 */
public class ContentView extends WebPage
{
  /** Label containg the content of the page. */
  protected Label content;
  
  /** Label containg the title of the page. */
  protected Label title;
  
  /** 
   * The folder which matches the request path. 
   * Usally set by {@link #generateSite(String)} 
   */
  transient protected Node currentPage;
    
  /** 
   * Creates a new instance of ContentView
   * 
   * @see #createAndAddComponents()  
   * @see #startRenderingPipeline() 
   */
  public ContentView()
  {
    createAndAddComponents();
    startRenderingPipeline();
  }
  
    
  /**
   * Use this function to create all needed components and add them to the page.
   * The default implementation will create and add
   * {@link #content} and {@link #title}.
   */
  protected void createAndAddComponents()
  {
    content = new Label("content", new Model(""));
    title = new Label("title", new Model(""));
    add(content);
    add(title);
  }
  
    /**
   * This function will determine the path which was requested and cleans it up.
   * It will then try to generate the site with this path. If the path does not
   * exists or an error occured a 404- or 500-page is generated.
   * 
   * @see #generateSite(java.lang.String) 
   * @see #generateFileNotFoundPage() 
   * @see #generateInternalErrorPage(java.lang.Exception) 
   */
  protected void startRenderingPipeline()
  {
    String path = this.getRequest().getPath();
    
    if(path == null)
    {
      path = "";
    }
    
    String cmsPath = ((GidooCMS) getApplication()).CMS_CONFIGURATION.getCMSPath();
    if(path.startsWith(cmsPath))
    {
      path = path.replaceFirst(cmsPath, "");
    }
    
    // some cleanup
    path = Tools.cleanupFolderPath(path);
    
    try
    {
      generateSite(path);
      if(currentPage != null)
      {
        ((GidooCMSSession) getSession()).setLastVisitedFolder(currentPage);
      }
    }
    catch(PathNotFoundException ex)
    {
      generateFileNotFoundPage();
    }
    catch(ContentGenerationFailedException ex)
    {
      generateInternalErrorPage(ex);
    }
  }
  
  /**
   * Uses the given path to create all content of the site. <br>
   * <br>
   * It will also try to find out whether the page exists or wether a resource
   * was requested.<br>
   * Override this to manipulate content generation.
   * @param path The path of the user request.
   * 
   * @see #generateContent(javax.jcr.Node) 
   * @see #generateTitle(javax.jcr.Node) 
   * @see #generateResourceDownload(javax.jcr.Node) 
   * 
   * @throws Thrown if there is no page or resource with the given path.
   */
  protected  void generateSite(String path) throws ContentGenerationFailedException, PathNotFoundException
  {      
    try
    {
      currentPage = JcrHelper.getPublishedPageByPath(
        path, ((GidooCMSSession) getSession()));
      
      if(currentPage != null)
      {
        final String requestPath = getRequest().getPath();
        if(!"".equals(requestPath) && !requestPath.endsWith("/"))
        {
          // force reload since pages without / can cause problemes with relative links
          WebApplication app = (WebApplication) getApplication();
          this.setResponsePage(new RedirectPage(
            app.getServletContext().getContextPath() + "/" + requestPath + "/"));
          return;
        }
              
        generateContent(currentPage);
        generateTitle(currentPage);
      }
      else
      {
        // try to find a resource
        Node res = 
          JcrHelper.getResourceByPath(path, (GidooCMSSession) getSession());
        if(res != null)
        {
          generateResourceDownload(res);
        }
        else
        {
          throw new PathNotFoundException();
        }
      }
    }
    catch (RepositoryException ex)
    {
      Logger.getLogger(ContentView.class.getName()).log(Level.SEVERE, null, ex);
      throw new ContentGenerationFailedException(ex.getMessage());
    }
  }
  
  /** Fills a label with the id "content" with the parsed content of the XML-file */
  protected  void generateContent(Node page) throws ContentGenerationFailedException
  {
    try
    {
      String html = 
        ((GidooCMS) getApplication()).transformContent(page.getProperty("cms:xml").getString());
      
      content.setDefaultModelObject(html);
      content.setEscapeModelStrings(false);
    }
    catch(RepositoryException ex)
    {
      throw new ContentGenerationFailedException(ex.getMessage());
    }
    catch(XSLTTransformFailedException ex)
    {
      throw new ContentGenerationFailedException(ex.getMessage());
    }
  }
  
  /** Fills a label with the id "title" with the parsed title of the XML-file */
  protected  void generateTitle(Node page)
  throws ContentGenerationFailedException
  {
    try
    {
      title.setDefaultModelObject(page.getProperty("cms:title").getString());
      title.setEscapeModelStrings(false);
    }
    catch(RepositoryException ex)
    {
      throw new ContentGenerationFailedException(ex.getMessage());
    }
  }
  
  /** Will generate a resource as the response to the request. */
  protected void generateResourceDownload(Node res)
  {
    CMSResourceStreamWriter writer = 
      new CMSResourceStreamWriter(res, (GidooCMS) getApplication());
    
    RequestCycle.get().setRequestTarget(
      new ResourceStreamRequestTarget(writer));
  }
  
  /** Generates a page informing the user, that the file was not found. */
  protected void generateFileNotFoundPage()
  {
    try
    {      
      title.setDefaultModelObject(getString("error404short"));
      content.setDefaultModelObject("<h1>" + getString("error404long") +"</h1>" +
        "<p>" + getString("filewasnotfound") + "</p>");
      content.setEscapeModelStrings(false);
      
      getWebRequestCycle().getWebResponse().getHttpServletResponse().setStatus(404);
    }
    catch(Exception ex)
    {
      generateInternalErrorPage(ex);
    }
  }
  
  /** Generates a page informing the user about an internal error*/
  protected  void generateInternalErrorPage(Exception ex)
  {
    // for the logs
    Logger.getLogger(ContentView.class.getName()).log(Level.SEVERE, null, ex);
    
    // for the user    
    title.setDefaultModelObject(getString("error500short"));
    
    StringWriter stackWriter = new StringWriter();
    ex.printStackTrace(new PrintWriter(stackWriter));
    
    content.setDefaultModelObject("<h1>" + getString("error500long") + "</h1>" +
      "<p>" + getString("internalservererror") + "</p>" +
      "<p>" + getString("reason") +"<br/>" + ex.getMessage() +
      "<p>" + getString("stacktrace") + "<br/>" + stackWriter.toString().replaceAll("\n", "<br/>"));
    content.setEscapeModelStrings(false);

    getWebRequestCycle().getWebResponse().getHttpServletResponse().setStatus(500);
  }
  
  /**
   * Sets the content and title for preview. Be careful when overriding this,
   * it may break the preview.
   * @param titleString The title as string.
   * @param contentString The content as string.
   */
  public void setPreviewContent(String titleString, String contentString)
  {
    content.setEscapeModelStrings(false);
    content.setDefaultModelObject(contentString);
    title.setDefaultModelObject(titleString);
    getWebRequestCycle().getWebResponse().getHttpServletResponse().setStatus(200);
  }
}
